import java.util.*;

public class PrefNeighbourCalThread extends Thread{
	private PeerData peerData;
	private SharedMessageQueue[] sharedMsg;
	private boolean[] UnchokedNeighbors = new boolean[5]; // TODO: replace with N; check if initialized to false by default
	private int optimisticallyUnchokedNeighbor;// peerID of OUN
	private PreferredNeighborsSelector PN;
	private OptimisticallyUnchokedSelector OUN;
	private int unchokingInterval, optUnchokeInterval;
	private int k; // numberOfPrefferedNeighbors
	private int numOfPeers;
	private Object lock;
	
	PrefNeighbourCalThread(PeerData peerData, SharedMessageQueue[] sharedMsg, int unchokingInterval, int optUnchokeInterval, int numberOfPrefferedNeighbors, int numOfPeers){
		this.peerData = peerData;
		this.sharedMsg = sharedMsg;
		this.unchokingInterval = unchokingInterval;
		this.optUnchokeInterval= optUnchokeInterval;
		k = numberOfPrefferedNeighbors;
		this.numOfPeers = numOfPeers;
		lock = new Object();
		PN = new PreferredNeighborsSelector(peerData);
		OUN = new OptimisticallyUnchokedSelector(peerData);
	}
	
	public void run(){
		PN.start();
		OUN.start();
		
		try {
			PN.join();
			OUN.join();
		} catch (InterruptedException ignore) {
		}
	}


class PreferredNeighborsSelector extends Thread{
	PeerData peerData;
	Message chokeMessage;
	Message unchokeMessage;
	int i=0;
	int[] unchokeTheseNeighbors = new int[5]; // TODO: replace 5 with k; null if no one interested; -1 indicates <k people interested
											  // contains peerNumbers
	
	PreferredNeighborsSelector(PeerData peerData){
		this.peerData = peerData;
		chokeMessage = new ChokeMessage();
		unchokeMessage = new UnchokeMessage();
	}
	
	public void run(){
		while(!peerData.checkForTermination()){
			// Select K nghbrs with highest download rate from I
				unchokeTheseNeighbors = peerData.topKNeighbors(k);
			// Reset rates to 0 except for OUN
				peerData.resetDownloadExcept(optimisticallyUnchokedNeighbor);
			
			synchronized(lock){
			// Send unchoke messages
				for(int peerNum=0; peerNum<k; peerNum++){
					if((unchokeTheseNeighbors[peerNum] != -1)&&(!UnchokedNeighbors[unchokeTheseNeighbors[peerNum]]) ){
						// TODO: check with sirisha
						sharedMsg[peerNum].enQueue(unchokeMessage);
						synchronized (sharedMsg[peerNum].dummyObject) {
							sharedMsg[peerNum].dummyObject.notify();
						}
						UnchokedNeighbors[peerNum] = true;
					}
				}
			
			// Send choke messages
				for(int peerNum=0; peerNum<numOfPeers; peerNum++){
					if( (UnchokedNeighbors[peerNum]) && (chokeInNextRound(peerNum)) ){
						// TODO: check with sirisha
						sharedMsg[peerNum].enQueue(chokeMessage);
						synchronized (sharedMsg[peerNum].dummyObject) {
							sharedMsg[peerNum].dummyObject.notify();
						}
						UnchokedNeighbors[peerNum] = false;
					}
				}
			} // Release lock
			
			try {
				Thread.sleep(unchokingInterval * 1000);
			} catch (InterruptedException e) {
				System.out.println("PrefNeighborCalThread Interrupted!!!!");
				e.printStackTrace();
			}
		}
	}
	
	boolean chokeInNextRound(int peerNumber){
		for(int i=0; i<k; i++){
			if(unchokeTheseNeighbors[i] == peerNumber)
				return true;
		}
		
		return false;
	}
}

class OptimisticallyUnchokedSelector extends Thread{
	PeerData peerData;
	int unchokeMe;
	Message unchokeMessage;
	
	public OptimisticallyUnchokedSelector(PeerData peerData) {
		this.peerData = peerData;
		unchokeMessage = new UnchokeMessage();
	}
	public void run(){
		while(!peerData.checkForTermination()){
			// Select one optimistically UN from I-UN-OUN
				synchronized (lock) {
					unchokeMe = peerData.nextOptUnchokedNeighbor(UnchokedNeighbors);
				}
			// TODO: check with sirisha
				sharedMsg[unchokeMe].enQueue(unchokeMessage);
				synchronized (sharedMsg[unchokeMe].dummyObject) {
					sharedMsg[unchokeMe].dummyObject.notify();
				}
				
				optimisticallyUnchokedNeighbor = unchokeMe;
			
				try {
					Thread.sleep(optUnchokeInterval * 1000);
				} catch (InterruptedException e) {
					System.out.println("optUnchokeNeighborCalThread Interrupted!!!!");
					e.printStackTrace();
				}
		}
	}
}

}